local ffi = require('ffi');
ffi.cdef[[
typedef struct GBASys GBASys;
typedef struct EmuVideo EmuVideo;
typedef struct ARM7TDMI ARM7TDMI;
typedef struct IO IO;
    bool CCPUReadGSASnapshot(GBASys *gba,const char *);
    bool CCPUReadGSASPSnapshot(GBASys *gba,const char *);
    bool CCPUWriteGSASnapshot(GBASys *gba,const char *, const char *, const char *, const char *);
    bool CCPUWriteBatteryFile(GBASys *gba,const char *);
    bool CCPUReadBatteryFile(GBASys *gba,const char *);
    bool CCPUExportEepromFile(const char *);
    bool CCPUImportEepromFile(GBASys *gba,const char *);
    void CapplyTimer(GBASys *gba,ARM7TDMI *cpu);
    void CCPULoop(GBASys *gba, EmuVideo *video, bool renderGfx, bool processGfx, bool renderAudio);
    
    void CCPUCleanUp();
    void CCPUUpdateRender(GBASys *gba);
    bool CCPUReadMemState(GBASys *gba, char *, int);
    bool CCPUReadState(GBASys *gba, const char *);
    bool CCPUWriteMemState(GBASys *gba, char *, int);
    bool CCPUWriteState(GBASys *gba, const char *);
    int  CCPULoadRom(GBASys *gba, const char *);
    int  CCPULoadRomWithIO(GBASys *gba, IO *);
    void CdoMirroring(GBASys *gba, bool);
    void CCPUUpdateRegister(ARM7TDMI *cpu, uint32_t, uint16_t);
    void CCPUInit(GBASys *gba, const char *,bool);
    void CCPUReset(GBASys *gba);
    void CCPUCheckDMA(GBASys *gba, ARM7TDMI *cpu, int,int);
    bool CCPUIsGBAImage(const char *);
    bool CCPUIsZipFile(const char *);

    int CCPULoadRomData(GBASys *gba,int size,void*romdata);
    
    //获得一些东西
    GBASys* CGetGBASys();
    uint16_t * CGetPix16(GBASys*gba);
    uint16_t *CGetKeyPtr();

    void (*SoundWriteFunction)(uint16_t*wave,int len,void*userdata);
    void *SoundWriteUserData;
]]

print('start load gba so');
local gba = loadso('emuex_vbam');
print('load gba so');
local sys = gba.CGetGBASys();
local keyptr = gba.CGetKeyPtr();
print(sys);
print(keyptr);

if(emuex==nil)then emuex = {}; end;

local s = CreateSound();

gba.SoundWriteFunction = function(wave,len,userdata)
    s:WriteWave(wave,len);
end

function emuex.LoadROM(fn)
    local game = {};
    local file = io.open(fn,"rb");
    --local rom = io.open([[d:\a.gba]],"rb"):read('*a');
    if(file==nil)then
        print('load rom error:',fn);
        return nil; 
    end;
    local rom = file:read('*a');
    file:close();
    print('start load rom',gba.CCPULoadRomData);
    game.romPath = fn;
    game.romDir,game.romFileName,game.romExt = GetFilePathInfo(fn);
    print("gba file:",game.romDir,game.romFileName,game.romExt);
    game.romSave = game.romDir .. game.romFileName .. '.sav';

    --assert(nil,tostring(gba.CCPULoadRomData));

    print("rom size:",string.format('%X\n',gba.CCPULoadRomData(ffi.cast('void*',sys),#rom,ffi.cast('void*',rom))));
    print('load rom');
        
    --参数gbasys, biosFile , userbiso 
    print(gba.CCPUInit(sys,nil,false));

    print(gba.CCPUReset(sys));

    --获得游戏速度
    function game:gameSpeed()
        return 0;
    end

    ----------------------------------------
    --加载游戏存档文件
    function game:ReadSaveFile(fn)
        if(fn==nil)then
            fn = game.romSave;
        end
        print('read save:',fn);
        gba.CCPUReadBatteryFile(sys,fn);
    end

    function game:WriteSaveFile(fn)
        if(fn==nil)then
            fn = game.romSave;
        end
        print('write save:',fn);
        gba.CCPUWriteBatteryFile(sys,fn);
    end
    ----------------------------------------

    local function getSaveStatePath(self,fn)
        local path = self.romDir .. self.romFileName .. tostring(fn) .. '.sgm';
        return path;
    end

    function game:GetSaveStateList()
        local list = {};
        for k=1,10 do
            local path = getSaveStatePath(self,k);
            local file = io.open(path,"rb");
            if(file)then
                print(path);
                table.insert(list,{path = path,index = k});
                file:close();
            end
        end
        return list;
    end

    function game:SaveState(fn)
        if(type(fn)=='string')then
            gba.CCPUWriteState(sys,fn);
        else
            gba.CCPUWriteState(sys,getSaveStatePath(self,fn));
        end
        print('save state',fn);
    end

    function game:LoadState(fn)
        if(type(fn)=='string')then
            gba.CCPUReadState(sys,fn);
        else
            gba.CCPUReadState(sys,getSaveStatePath(self,fn));
        end
        print('load state',fn);
    end

    function game:SetKey(key)
        keyptr[0] = bit.bnot(key);
    end

    function game:main()
        local pix16 = gba.CGetPix16(sys);
        if(self.draw16)then
            self:draw16(pix16,240,160,16);
        end
        --drawPix16(pix16,240,160);

        --
        gba.CCPULoop(sys,nil,0,1,1);
    end
    --读取存档文件

    return game;
end




--测试
-- SDL_PIXELFORMAT_RGB555=353570562,
-- SDL_PIXELFORMAT_BGR555=357764866,
local test = false;
if(test)then
    local game = emuex.LoadROM([[d:\a.gba]]);
    function game:draw16(pix,w,h,c) return drawPix16(pix,w,h); end;
    local bor = bit.bor;
    SDL2 = ffi.load('SDL2');
    local width = 240;
    local height = 160;
    local window = SDL2.SDL_CreateWindow("游戏",100,100,width,height,bor(SDL2.SDL_WINDOW_OPENGL,SDL2.SDL_WINDOW_SHOWN));
    local event = ffi.new('SDL_Event');
    local srcRect = ffi.new('SDL_Rect');
    local dstRect = ffi.new('SDL_Rect');
    local index = 2; --win32 opengl
    if(ANDROID)then
        index = -1;
    end
    local renderer = SDL2.SDL_CreateRenderer(window,index,bor(SDL2.SDL_RENDERER_TARGETTEXTURE,SDL2.SDL_RENDERER_ACCELERATED));
    texture = SDL2.SDL_CreateTexture(renderer,SDL2.SDL_PIXELFORMAT_RGBA8888,SDL2.SDL_TEXTUREACCESS_STATIC,width,height);



    function drawPix16(pix16,w,h)
        if(texture16==nil)then
            local fmt = SDL2.SDL_PIXELFORMAT_BGR555;
            local access = SDL2.SDL_TEXTUREACCESS_TARGET;
            local r = renderer;
            local texture = SDL2.SDL_CreateTexture(r,fmt,access,w,h);
            SDL2.SDL_SetTextureBlendMode(texture,SDL2.SDL_BLENDMODE_BLEND);
            texture16 = ffi.gc(texture,SDL2.SDL_DestroyTexture);
        end
        srcRect.x = 0;
        srcRect.y = 0;
        srcRect.w = w;
        srcRect.h = h;
        
        --SDL2.SDL_memset(pix16,0x60,240*80);
        SDL2.SDL_UpdateTexture(texture16,srcRect,pix16,2*w);
        print('render',SDL2.SDL_RenderCopy(renderer,texture16,srcRect,dstRect));
    end

    while(true)do
        game:main();
        while(SDL2.SDL_PollEvent(event)~=0)do
            --self:SetTitle(string.format('%X',event.type));
        end
        if(event.type==SDL2.SDL_QUIT)then
            break;
        end
        SDL2.SDL_RenderPresent(renderer);
    end

end

return emuex;

